﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml.Linq;
using System.Collections;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
using System.Reflection;
using Twodi.Structs;
using System.Dynamic;

namespace Twodi.Utility
{
    public class Helper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(Object obj)
        {
            if (obj == null)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(TimeSpan obj)
        {
            if (obj == null || obj == TimeSpan.Zero)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(double obj)
        {
            if (obj == 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(String obj)
        {
            if (obj == null || obj == "")
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(IList obj)
        {
            if (obj == null || obj.Count == 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isEmpty(Point obj)
        {
            if (obj == null || (obj.X < 0 && obj.Y < 0))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Retorna valor reStringido pelos limites. Similar ao Clamp.
        /// </summary>
        /// <param name="value">Valor desejado.</param>
        /// <param name="minValue">Minimo valor possivel.</param>
        /// <param name="maxValue">Maximo valor possivel.</param>
        /// <returns></returns>
        public static int insideLimits(int value, int minValue, int maxValue)
        {
            return (int)MathHelper.Clamp(value, minValue, maxValue);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static String maskedString(String message, params object[] parameters)
        {
            for (int i = 0; i < parameters.Length; i++)
            {
                message = message.Replace("{" + i + "}", parameters[i].ToString());
            }
            return message;
        }

        /// <summary>
        /// Creates an Dictionary of Sprites, using as Key its Default Name + X + Y shadowPosition.
        /// </summary>
        /// <param name="shadowPosition">Position to be used as reference.</param>
        /// <param name="numberSprites">Number of Columns and Lines.</param>
        /// <param name="size">Size of Sprite.</param>
        /// <param name="defaultName">Default Name to be used as key.</param>
        /// <returns>Dictionary of Sprites</returns>
        public static Dictionary<String, Rectangle> createSprites(Point position, Point numberSprites, Point size, String defaultName = "Sprite")
        {
            Dictionary<String, Rectangle> sprites = new Dictionary<String, Rectangle>();

            for (int y = 0; y < numberSprites.Y; y++)
            {
                for (int x = 0; x < numberSprites.X; x++)
                {
                    sprites.Add(defaultName + x.ToString() + ":" + y.ToString(), new Rectangle(position.X + x * size.X, position.Y + y * size.Y, size.X, size.Y));
                }
            }

            return sprites;
        }

        /// <summary>
        /// Creates an Dictionary of Sprites, using as Key its Default Name + X + Y shadowPosition.
        /// </summary>
        /// <param name="shadowPosition">Position to be used as reference.</param>
        /// <param name="numberSprites">Number of Columns and Lines.</param>
        /// <param name="size">Size of Sprite.</param>
        /// <param name="defaultName">Default Name to be used as key.</param>
        /// <returns>Dictionary of Sprites</returns>
        public static Dictionary<String, Rectangle> createSprites(Point numberSprites, Point size, String defaultName = "Sprite")
        {
            Dictionary<String, Rectangle> sprites = new Dictionary<String, Rectangle>();

            for (int y = 0; y < numberSprites.Y; y++)
            {
                for (int x = 0; x < numberSprites.X; x++)
                {
                    sprites.Add(defaultName + x.ToString() + ":" + y.ToString(), new Rectangle(x * size.X, y * size.Y, size.X, size.Y));
                }
            }

            return sprites;
        }

        public static Sprites createSpritesModel(Point numberSprites, Point size, String defaultName = "Sprite")
        {
            Sprites sprites = new Sprites();

            for (int y = 0; y < numberSprites.Y; y++)
            {
                for (int x = 0; x < numberSprites.X; x++)
                {
                    Sprite temp = new Sprite(defaultName + x.ToString() + ":" + y.ToString(), new Rectangle(x * size.X, y * size.Y, size.X, size.Y));
                    sprites.Add(temp);
                }
            }

            return sprites;
        }

        public static Sprites createSpritesModel(Point imageSize, Point size, Point grid, String defaultName = "Sprite", bool continuousNumber = false, int startIndex = 0)
        {
            Sprites sprites = new Sprites();
            Rectangle sprite = new Rectangle();
            Point factor = new Point();

            bool keepCreating = true;
            int maxValue = 0;
            while (keepCreating)
            {
                sprite = new Rectangle(factor.X * (size.X + grid.X), factor.Y * (size.Y + grid.Y), size.X, size.Y);

                String key = defaultName;
                if (continuousNumber)
                {
                    if (factor.X > maxValue)
                    {
                        maxValue = factor.X;
                    }
                    int number = (factor.Y * (maxValue + 1)) + (factor.X + startIndex);
                    key += number.ToString();
                }
                else
                {
                    key += factor.X.ToString() + ":" + factor.Y.ToString();
                }

                if (sprite.X + sprite.Width >= imageSize.X)
                {
                    factor.X = 0;
                    factor.Y++;
                }
                else if (sprite.Y + sprite.Width > imageSize.Y)
                {
                    keepCreating = false;
                    break;
                }
                else
                {
                    factor.X++;
                }

                Sprite temp = new Sprite(key, sprite);
                sprites.Add(temp);
            }

            return sprites;
        }

        /// <summary>
        /// Measure text passed, using font defined at TextModel.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static Vector2 measureString(SpriteFont font, String text)
        {
            return font.MeasureString(text);
        }

        public static Texture2D CreateCircleTexture(Game game, int radius, Color outerColor)
        {
            int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
            Texture2D texture = new Texture2D(game.GraphicsDevice, outerRadius, outerRadius);

            Color[] data = new Color[outerRadius * outerRadius];

            // Colour the entire texture transparent first.
            for (int i = 0; i < data.Length; i++)
                data[i] = Color.Transparent;

            // Work out the minimum step necessary using trigonometry + sine approximation.
            double angleStep = 1f / radius;

            for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
            {

                int x = (int)Math.Round(radius + radius * Math.Cos(angle));
                int y = (int)Math.Round(radius + radius * Math.Sin(angle));

                data[y * outerRadius + x + 1] = outerColor;
            }

            texture.SetData(data);
            return texture;
        }

        public static Texture2D CreateCircleTexture(Game game, int radius, Color outerColor, Color innerColor)
        {
            int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
            Texture2D texture = new Texture2D(game.GraphicsDevice, outerRadius, outerRadius);

            Color[] data = new Color[outerRadius * outerRadius];

            // Colour the entire texture transparent first.
            for (int i = 0; i < data.Length; i++)
                data[i] = Color.Transparent;

            // Work out the minimum step necessary using trigonometry + sine approximation.
            double angleStep = 1f / radius;

            for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
            {

                int x = (int)Math.Round(radius + radius * Math.Cos(angle));
                int y = (int)Math.Round(radius + radius * Math.Sin(angle));

                data[y * outerRadius + x + 1] = outerColor;
            }

            //Fill the ball.
            bool finished = false;
            int firstSkip = 0;
            int lastSkip = 0;

            for (int i = 0; i <= data.Length - 1; i++)
            {
                if (finished == false)
                {
                    if ((data[i] == outerColor) && (firstSkip == 0))
                    {
                        while (data[i + 1] == outerColor)
                        {
                            i++;
                        }
                        firstSkip = 1;
                        i++;
                    }
                    if (firstSkip == 1)
                    {
                        if (data[i] == outerColor && data[i + 1] != outerColor)
                        {
                            i++;
                            while (data[i] != outerColor)
                            {
                                for (int j = 1; j <= outerRadius; j++)
                                {
                                    if (data[i + j] != outerColor)
                                    {
                                        lastSkip++;
                                    }
                                }

                                if (lastSkip == outerRadius)
                                {
                                    finished = true;
                                    break;
                                }
                                else
                                {
                                    data[i] = innerColor;
                                    i++;
                                    lastSkip = 0;
                                }
                            }
                            while (data[i] == outerColor)
                            {
                                i++;
                            }
                            i--;
                        }

                    }
                }
            }

            texture.SetData(data);
            return texture;
        }

        public static Texture2D CreateSquareTexture(Game game, int width, int height, Color innerColor)
        {
            return CreateSquareTexture(game, width, height, innerColor, innerColor);
        }

        public static Texture2D CreateSquareTexture(Game game, int width, int height, Color innerColor, Color borderColor)
        {
            Texture2D tempTexture;
            tempTexture = new Texture2D(game.GraphicsDevice, width, height);

            Color[] textColors = new Color[width * height];

            tempTexture.GetData<Color>(textColors);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (y == 0 || y == width - 1 ||
                        x == 0 || x == width - 1)
                    {
                        textColors[y * width + x] = borderColor;
                    }
                    else
                    {
                        textColors[y * width + x] = innerColor;
                    }
                }
            }

            tempTexture.SetData<Color>(textColors);

            return tempTexture;
        }

        public static Vector2 DetermineDirection(Vector2 origin, Vector2 destination)
        {
            Vector2 result = Vector2.Zero;
            if (origin.X < destination.X)
            {
                result.X = -1;
            }
            else if (origin.X > destination.X)
            {
                result.X = 1;
            }

            if (origin.Y < destination.Y)
            {
                result.Y = -1;
            }
            else if (origin.Y > destination.Y)
            {
                result.Y = 1;
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <returns></returns>
        public static Color[,] TextureTo2DArray(Texture2D texture)
        {
            Color[] colors1D = new Color[texture.Width * texture.Height];
            texture.GetData(colors1D);

            Color[,] colors2D = new Color[texture.Width, texture.Height];
            for (int x = 0; x < texture.Width; x++)
                for (int y = 0; y < texture.Height; y++)
                    colors2D[x, y] = colors1D[x + y * texture.Width];

            return colors2D;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static Point calculateCoordenate(int width, int height, int value)
        {
            int x = value % width;
            int y = (value - x) / width;

            return new Point(x, y);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool isNot<T>(object obj)
        {
            if (obj is T)
            {
                return false;
            }
            return true;
        }

        /**
        public static System.Drawing.Image ConvertTextureToImage(Texture2D texture)
        {
            if (texture == null)
            {
                return null;
            }

            if (texture.IsDisposed)
            {
                return null;
            }

            //Memory stream to store the bitmap data.
            MemoryStream ms = new MemoryStream();

            //Save the texture to the stream.
            texture.SaveAsPng(ms, texture.Width, texture.Height);

            //Seek the beginning of the stream.
            ms.Seek(0, SeekOrigin.Begin);

            //Create an image from a stream.
            System.Drawing.Image bmp2 = System.Drawing.Bitmap.FromStream(ms);

            //Close the stream, we nolonger need it.
            ms.Close();
            ms = null;
            return bmp2;
        }
         * **/

        public static Rectangle getViewPortBounds(Game game)
        {
            Viewport viewport = game.GraphicsDevice.Viewport;
            return new Rectangle(viewport.X, viewport.Y, viewport.Width, viewport.Height);
        }

        /// <summary>
        /// Returns an vector with the direction from the angle.
        /// </summary>
        /// <param name="up">Direction where is "UP"</param>
        /// <param name="angle">LimitedComponent's Angle</param>
        /// <returns></returns>
        public static Vector2 directionFromAngle(Vector2 up, float angle)
        {
            Matrix rotationMatrix = Matrix.CreateRotationZ(angle);
            return Vector2.Transform(up, rotationMatrix);
        }

        /// <summary>
        /// Returns an vector with the direction from the angle. "UP" direction is defined as (0, -1);
        /// </summary>
        /// <param name="angle">LimitedComponent's Angle</param>
        /// <returns></returns>
        public static Vector2 directionFromAngle(float angle)
        {
            Matrix rotationMatrix = Matrix.CreateRotationZ(angle);
            return Vector2.Transform(-Vector2.UnitY, rotationMatrix);
        }

        /// <summary>
        /// Organize list by property value.
        /// </summary>
        /// <typeparam name="T">List type.</typeparam>
        /// <param name="propertyName">Property name.</param>
        /// <param name="list">List to be organized.</param>
        /// <returns>List organized.</returns>
        public static List<T> OrganizeByProperty<T>(string propertyName, IList list)
        {
            List<T> List = FilterByType<T>(list);
            return List.OrderBy(o => o.GetType().GetProperty(propertyName).GetValue(o, new object[0])).ToList();
        }

        /// <summary>
        /// Returns a new list with objects only with the same time used on T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public static List<T> FilterByType<T>(IList list)
        {
            List<T> List = new List<T>();

            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] is T)
                {
                    List.Add((T)list[i]);
                }
            }

            return List;
        }

        /// <summary>
        /// Transforma posição no mundo para posição referente ao componente.
        /// 1) Inverte a matrix de transformação do componente.
        /// 2) Transforma a posição do mundo com a matrix inversa: Vector2.Transform(worldPosition, Matrix.Invert(componentTransformation))
        /// </summary>
        /// <param name="worldPosition">Posição no mundo.</param>
        /// <param name="componentTransformation">Matrix de transformação do componente.</param>
        /// <returns>Posição referente ao componente.</returns>
        public static Vector2 TransformPosition(Vector2 worldPosition, Matrix componentTransformation)
        {
            return Vector2.Transform(worldPosition, Matrix.Invert(componentTransformation));
        }

        /// <summary>
        /// Pegar serviço diretamente do jogo.
        /// </summary>
        /// <typeparam name="T">Tipo do serviço.</typeparam>
        /// <param name="game">Jogo cujo serviço vai ser extraido.</param>
        /// <returns></returns>
        public static T GetService<T>(Game game)
        {
            return (T)game.Services.GetService(typeof(T));
        }

        public static Vector2 MiddlePosition(float aWidth, float aHeight, float bWidht, float bHeigt)
        {
            return new Vector2((aWidth - bWidht) / 2, (aHeight - bHeigt) / 2);
        }

        class GameConfiguration : DynamicObject
        {

        }

        public static void TypeList(Assembly gameAssembly, Type type)
        {
            var types = gameAssembly.GetTypes();

            for (int i = 0; i < types.Length; i++)
            {
                if (types[i].GetInterfaces().Contains(type))
                {
                    Console.WriteLine("Contains");
                };
            }
        }

    }
}
